<?php
namespace pay\wechat;

use WeChatPay\Builder;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Util\PemUtil;
use WeChatPay\Formatter;
use think\Db;
use WeChatPay\Crypto\AesGcm;
class WxPay
{
    public $merchantId = '190000****';
    // 从本地文件中加载「商户API私钥」，「商户API私钥」会用来生成请求的签名
    public $merchantPrivateKeyFilePath = 'file:///path/to/merchant/apiclient_key.pem';
    public $merchantPrivateKeyInstance = '';
    // 「商户API证书」的「证书序列号」
    public $merchantCertificateSerial = '3775B6A45ACD588826D15E583A95F5DD********';
    // 从本地文件中加载「微信支付平台证书」，用来验证微信支付应答的签名
    public $platformCertificateFilePath = ROOT_PATH.'extend'.DS.'pay'.DS.'wx'.DS.'plate.pem';
    public $platformPublicKeyInstance = '';
    // 从「微信支付平台证书」中获取「证书序列号」
    public $platformCertificateSerial = '';
    public $instance;
    public $notify_url = '';
    public $appid = '';
    public $mchkey = '';//apiv3密钥
    public function __construct(){
        $pay_way = Db::name('pay_way')->where(['type'=>'weixin'])->find();
        $config = json_decode($pay_way['config'],true);
        $this->merchantId = $config['mer_id'];
        // 从本地文件中加载「商户API私钥」，「商户API私钥」会用来生成请求的签名
        $private_key = file_get_contents(ROOT_PATH.$config['private_key']);
        $this->merchantPrivateKeyFilePath = $private_key;
        $this->merchantPrivateKeyInstance = Rsa::from($this->merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
        // 「商户API证书」的「证书序列号」
        $this->merchantCertificateSerial = $config['ficate_serial'];
        // 从本地文件中加载「微信支付平台证书」，用来验证微信支付应答的签名
        $plate_pem = file_get_contents( $this->platformCertificateFilePath);
        $this->platformCertificateFilePath = $plate_pem;
        $this->platformPublicKeyInstance = Rsa::from($this->platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
        // 从「微信支付平台证书」中获取「证书序列号」
        $this->platformCertificateSerial = PemUtil::parseCertificateSerialNo($this->platformCertificateFilePath);
        //php -f ./CertificateDownloader.php -- -k pzqgrz6dvm87k9wqnr2ddgh7fddc1mx0 -m 1496188142 -f E:\phpstudy_pro\WWW\yhrnew\extend\pay\wx\apiclient_key.pem -s 468734679F83E003825B04D66C2D6A39DBA86844 -o E:\phpstudy_pro\WWW\yhrnew\extend\pay\wx\
        // 构造一个 APIv3 客户端实例
        $this->instance = Builder::factory([
            'mchid'      => $this->merchantId,
            'serial'     => $this->merchantCertificateSerial,
            'privateKey' => $this->merchantPrivateKeyInstance,
            'certs'      => [
                $this->platformCertificateSerial => $this->platformPublicKeyInstance,
            ],
        ]);
        $this->notify_url = getCurUrl().'/home/Notify/notify_wx_refund';
        $this->appid = $config['appid']??'';
        $this->mchkey = $config['mchkey']??'';
    }
    public function h5_pay($rsorder){
        // 以 Native 支付为例，发送请求
        try {
            $params = [
                'mchid'        => $this->merchantId,
                'out_trade_no' => $rsorder['order_no'],
                'appid'        =>  $this->appid,
                'description'  => $rsorder['payname'],
                'notify_url'   =>  $this->notify_url,
                'amount'       => [
                    'total'    => $rsorder['pay_price'],
                    'currency' => 'CNY'
                ],
                'scene_info' => [
                    'payer_client_ip'=>'',
                    'h5_info' => [
                        'type' => 'Wap',
                    ]
                ],
            ];
            $resp = $this->instance
                ->chain('/v3/pay/transactions/h5')
                ->post(['json' => $params]);

            $status_code =  $resp->getStatusCode();
            $body = $resp->getBody();
            $return_data = json_decode($body,true);
            writeLog('请求成功','wx_pay');
            writeLog(json_encode($resp),'wx_pay');
            writeLog($body,'wx_pay');
            if($status_code==200 && !empty($return_data['h5_url'])){
                return ['status'=>1,'msg'=>'','data'=>$return_data['h5_url']];
            }else{
                return ['status'=>0,'msg'=>'支付失败'];
            }
        } catch (\Exception $e) {
            // 进行错误处理
            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
                $r = $e->getResponse();
                $res = $r->getBody();
                writeLog('请求失败','wx_pay');
                writeLog(json_encode($r),'wx_pay');
                writeLog($res,'wx_pay');
                echo $res;exit;
                $result = json_decode($res,true);
            }
//            dump($e);exit;
//            echo $e->getTraceAsString(), PHP_EOL;
        }
    }
    /**
     * @param $data
     * @return \Psr\Http\Message\StreamInterface
     */
    public function native_pay($rsorder){
        // 以 Native 支付为例，发送请求
        $resp = $this->instance
            ->chain('v3/pay/transactions/native')
            ->post(['json' => [
                'mchid'        => $this->merchantId,
                'out_trade_no' => $rsorder['order_no'],
                'appid'        =>  $this->appid,
                'description'  => $rsorder['payname'],
                'notify_url'   =>  $this->notify_url,
                'amount'       => [
                    'total'    => $rsorder['pay_price'],
                    'currency' => 'CNY'
                ],
            ]]);
        $response = $resp->getBody();
        dump($response);exit;
    }
    public function app_pay($rsorder){
        try {
            $params = [
                'mchid' => $this->merchantId,
                'out_trade_no' => $rsorder['order_no'],
                'appid' => $this->appid,
                'description' => $rsorder['payname'],
                'notify_url' => $this->notify_url,
                'amount' => [
                    'total' => $rsorder['pay_price'],
                    'currency' => 'CNY'
                ],
            ];
            $resp = $this->instance
                ->chain('v3/pay/transactions/app')
                ->post(['json' => $params]);
            $status_code =  $resp->getStatusCode();
            $body = $resp->getBody();
            $return_data = json_decode($body,true);
            if($status_code==200 && !empty($return_data['prepay_id'])){
                $wOpt = [];
                $wOpt['appid'] = $this->appid;
                $wOpt['timestamp'] = (string)Formatter::timestamp();
                $wOpt['noncestr'] = Formatter::nonce();
                $wOpt['prepayid'] = $return_data['prepay_id'];
//                $wOpt['signType'] = 'HMAC-SHA256';
//                $mchkey = $this->mchkey;
//                ksort($wOpt, SORT_STRING);
//                foreach ($wOpt as $key => $v) {
//                    $string .= "{$key}={$v}&";
//                }
//                $string .= "key=".$mchkey;
//                $sign = strtoupper(md5($string));
                    //参与签名字段及格式：
                    //
                    //应用id
                    //时间戳
                    //随机字符串
                    //预支付交易会话ID
                writeLog(json_encode(array_values($wOpt)),'wx_pay');
                writeLog(json_encode(array_merge(array_values($wOpt), [''])),'wx_pay');
                writeLog(Formatter::joinedByLineFeed(...array_values($wOpt)),'wx_pay');
                writeLog(json_encode(Formatter::joinedByLineFeed(...array_values($wOpt))),'wx_pay');
                $sign = Rsa::sign(Formatter::joinedByLineFeed(...array_values($wOpt)), $this->merchantPrivateKeyInstance);
                $wOpt['sign'] = $sign;
                $wOpt['partnerid'] = $this->merchantId;

                $wOpt['package'] = 'Sign=WXPay';
                writeLog(json_encode($wOpt,JSON_UNESCAPED_UNICODE),'wx_pay');
                return ['status'=>1,'msg'=>'','data'=>$wOpt];
            }else{
                return ['status'=>0,'msg'=>'支付失败'];
            }
        } catch (\Exception $e) {
            // 进行错误处理
            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
                $r = $e->getResponse();
                $res = $r->getBody();
                echo $res;exit;
                $result = json_decode($res,true);
               dump($result);exit;
            }
//            dump($e);exit;
//            echo $e->getTraceAsString(), PHP_EOL;
        }
    }

    public function notify_wx($header, $inBody)
    {
        $inWechatpaySignature = $header['wechatpay-signature'];
        $inWechatpayTimestamp = $header['wechatpay-timestamp'];
        $inWechatpaySerial = $header['wechatpay-serial'];
        $inWechatpayNonce = $header['wechatpay-nonce'];
        if (!$inWechatpaySignature or !$inWechatpayTimestamp or !$inWechatpaySerial or !$inWechatpayNonce) {
            header("Location:/404.html");
            exit;
        }
        $merchantId = $this->merchantId ?? '';     // 商户号
        $apiv3Key = $this->mchkey ?? '';// 在商户平台上设置的APIv3密钥
        $certPath = 'file://' . ROOT_PATH . 'cert/wx/' . $merchantId;
        // 根据通知的平台证书序列号，查询本地平台证书文件，这里是自己生成的证书
        $platformPublicKeyInstance = Rsa::from($this->platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
        // 检查通知时间偏移量，允许5分钟之内的偏移
        $timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
        writeLog('验签inbody'.$inBody,'notify');
        writeLog('签名字符串'.Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),'notify');
        writeLog('签名'.$inWechatpaySignature,'notify');
        $verifiedStatus = Rsa::verify(
        // 构造验签名串
            Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
            $inWechatpaySignature,
            $platformPublicKeyInstance
        );
        if ($timeOffsetStatus && $verifiedStatus) {
            // 转换通知的JSON文本消息为PHP Array数组
            $inBodyArray = (array)json_decode($inBody, true);
            // 使用PHP7的数据解构语法，从Array中解构并赋值变量
            ['resource' => [
                'ciphertext' => $ciphertext,
                'nonce' => $nonce,
                'associated_data' => $aad
            ]] = $inBodyArray;
            // 加密文本消息解密
            $inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
            // 把解密后的文本转换为PHP Array数组
            return (array)json_decode($inBodyResource, true);
        } else {
            return ['trade_state' => 'FAIL'];
        }
    }

    //退款
    public function refund($rsorder){
        try {
            $params = [
                'transaction_id' => $this->merchantId,//【微信支付订单号】 原支付交易对应的微信订单号
                'out_trade_no' => $rsorder['order_no'],//【商户订单号】 原支付交易对应的商户订单号，与transaction_id二选一
                'out_refund_no' => $this->appid,//【商户退款单号】 商户系统内部的退款单号，商户系统内部唯一，只能是数字、大小写字母_-|*@ ，同一退款单号多次请求只退一笔。
                'reason' => $rsorder['payname'],
                'notify_url' => $this->notify_url,
                'amount' => [
                    'refund' => $rsorder['pay_price'],//【退款金额】 退款金额，单位为分，只能为整数，不能超过原订单支付金额。
                ],
            ];
            $resp = $this->instance
                ->chain('/v3/refund/domestic/refunds')
                ->post(['json' => $params]);
            $status_code =  $resp->getStatusCode();
            $body = $resp->getBody();
            $return_data = json_decode($body,true);
            if($status_code==200 && !empty($return_data['status'])){

                return ['status'=>1,'msg'=>'','refund_id'=>$return_data['refund_id']];
            }else{
                return ['status'=>0,'msg'=>'退款失败'];
            }
        } catch (\Exception $e) {
            // 进行错误处理
            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
                $r = $e->getResponse();
                $res = $r->getBody();
                echo $res;exit;
                $result = json_decode($res,true);
                dump($result);exit;
            }
//            dump($e);exit;
//            echo $e->getTraceAsString(), PHP_EOL;
        }
    }
    public function test(){
        $wOpt = [];
        $wOpt['appid'] = 'wx8b1d39695a042c82';
        $wOpt['timestamp'] = '1721011502';
        $wOpt['noncestr'] = '43BZmafAPj8sYH3kqdmv7dTyqvrMzvV1';
        $wOpt['prepayid'] = 'wx1510450263270891ba21e75cb2c3040000';

//        dump(array_values($wOpt));
//        dump(array_values($wOpt));
//        dump(array_merge(array_values($wOpt), ['']));
//        $sign_array = array_values($wOpt);
//        dump(Formatter::joinedByLineFeed($sign_array));
//        dump(json_encode(Formatter::joinedByLineFeed(array_values($wOpt))));
        $sign = Rsa::sign(Formatter::joinedByLineFeed(...array_values($wOpt)), $this->merchantPrivateKeyInstance);
        dump($sign);exit;
    }
}
